package com.example.webfluxdemo;

import com.alibaba.fastjson.JSONObject;
import com.example.webfluxdemo.domain.AdSpaceEntity;
import com.example.webfluxdemo.domain.AdSpaceRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;

import java.io.IOException;
import java.net.URI;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

@RestController
public class HelloController {
    @Value("${bidTotalTimeout:0}")
    private String bidTotalTimeout;
    private final ReactiveRedisTemplate<String, Object> redisTemplate;
    private final AdSpaceRepository adSpaceRepository;
    public HelloController(ReactiveRedisTemplate<String, Object> redisTemplate, AdSpaceRepository adSpaceRepository) {
        this.redisTemplate = redisTemplate;
        this.adSpaceRepository = adSpaceRepository;
    }

    @GetMapping("hello")
    public String hello(String name) {
        return "Hello " + name + ": " + bidTotalTimeout;
    }

    @GetMapping("ad_space")
    public Flux<AdSpaceEntity> findAll(int status) {
        // Flux<String> f = Flux.fromArray(new String[]{"Hello", "AdSpace"});
        return adSpaceRepository.findByStatus(status);
    }
    @GetMapping("ad_space/{identity}")
    public Mono<AdSpaceEntity> findByIdentity(@PathVariable String identity) {
        String key = "webflux:ad_space:" + identity;
        return redisTemplate.opsForValue().get(key).switchIfEmpty(findByIdentityFromDb(identity)).cast(AdSpaceEntity.class);
    }

    /**
     * 302到百度
     * @param response
     * @return
     */
    @GetMapping("baidu")
    public Mono<Void> baidu(ServerHttpResponse response) {
        response.setStatusCode(HttpStatus.PERMANENT_REDIRECT);
        response.getHeaders().setLocation(URI.create("https://www.baidu.com"));
        return response.setComplete();
    }

    @GetMapping("error")
    public Mono<String> error() throws IOException {
        throw new IOException("test io error");
    }
    @GetMapping("error2")
    public Mono<String> error2() {
        return Mono.just("hello").flatMap(s -> Mono.error(new IOException("My fault!")));
    }
    @GetMapping("redis")
    public Mono<String> redis() {
        testRedis();
        return Mono.just("Hello World");
    }

    /**
     * 测试mono的subscribe在非Mono方法中是否起作用
     */
    private void testRedis() {
        redisTemplate.opsForValue().set("foo", "bar").subscribe();
        redisTemplate.expire("foo", Duration.ofSeconds(60)).subscribe();
    }

    private Mono<AdSpaceEntity> findByIdentityFromDb(String identity) {
        Mono<AdSpaceEntity> mono = adSpaceRepository.findByIdentityId(identity);
        return mono.flatMap(e -> {
            String key = "webflux:ad_space:" + identity;
            redisTemplate.opsForValue().set(key, e, Duration.ofSeconds(60)).subscribe();
            return Mono.just(e);
        });
    }

    @GetMapping("testDelay")
    public Flux<Integer> testDelay() {
        List<Integer> lesser10List = new ArrayList<>();
        Flux.fromStream(Stream.of(4, 5, 10, 11, 3, 12)).flatMap(i -> lesser10(i).doOnNext(b -> {
            if (!b) lesser10List.add(i);
        })).elapsed().doOnNext(tl -> System.out.println("elapsed: " + tl.getT1() + "ms")).map(Tuple2::getT2).all(r -> r).subscribe(r -> {
            System.out.println("lesser10List: " + JSONObject.toJSONString(lesser10List));
        });
        return Flux.fromIterable(lesser10List);
//        List<Integer> lesser10List2 = new ArrayList<>();
//        Flux.fromStream(Stream.of(4, 5, 10, 1, 3, 12)).collectList().subscribe(list -> {
//            for (Integer i : list) {
//                lesser10(i).subscribe(b -> {
//                    if (!b) lesser10List2.add(i);
//                });
//            }
//        });
//        return Flux.fromIterable(lesser10List2);
    }

    private Mono<Boolean> lesser10(Integer i) {
        System.out.println("In lesser10: " + i);
        return Mono.defer(() -> {
            List<Integer> list = new ArrayList<>();
            for (int j = 0; j < 1000000; j++) {
                list.add(j);
            }
            return Mono.just(list);
        }).flatMap(l -> Mono.just(i < 10));
    }
}
